﻿/*
    Project: IV:MP GUI Editor (http://code.google.com/p/ivmp-gui-editor)
    Code license: GNU Lesser GPL (http://www.gnu.org/copyleft/lesser.html) 
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

// FIXME: more async, move any .save() and etc to ExecuteIDECommand()!

namespace IvmpEditor
{
    public partial class wndMain : Window
    {
        Editor EditorMgr;
        WindowInteropHelper wndHelper;
        public wndMain()
        {
            InitializeComponent();
            menuShowAboutBox.Header = string.Format((string)menuShowAboutBox.Header, "IV:MP GUI Editor");
            EditorMgr = new Editor(this);
            wndHelper = new WindowInteropHelper(this);

            // Set window size, state and position after XAML load:
            this.SourceInitialized += delegate
            {
                if (Settings.RememberWindowState)
                {
                    if (Settings.MainWindowMaximized)
                        this.WindowState = WindowState.Maximized;
                    else
                    {
                        var size = Settings.MainWindowSize;
                        this.Width = size.Width;
                        this.Height = size.Height;
                    }
                    var position = Settings.MainWindowPosition;
                    this.Left = position.X;
                    this.Top = position.Y;
                }

                // Load menu with preffered (favorite) encodings:
                UpdateFavoriteEncodings();
                UpdateRecentFiles();
            };

            // Check if app is already loaded: following code is initialization code.
            if (!App.lockLoading.IsHolden)
                return;

            // If needed, run named pipes server to receive commands from future instances
            if (Settings.MultiTabbedInterface)
                NamedPipes.RunServer();

            // Process startup commandline
            string[] starupCmd = Environment.GetCommandLineArgs();
            if (starupCmd.Length > 1)
            {
                var cmd = eIDECommand.OpenFileAuto;
                var cmdArgs = string.Empty;
                for (var i = 1; i < starupCmd.Length; i++)
                    cmdArgs += starupCmd[i] + (i < starupCmd.Length - 1 ? " " : string.Empty);
                ExecuteIDECommand(cmd, cmdArgs);
            }
            else
                ExecuteIDECommand(eIDECommand.OpenEmptyTab);

            // Release loading lock:
            App.lockLoading.Release();
        }

        // Methods:
        public bool CloseTab(ETab tab)
        {
            bool b;
            CloseTab(tab, out b);
            return !b;
        }
        public void CloseTab(ETab tab, out bool cancelled)
        {
            cancelled = false;

            CurrentTab = tab;
            if (tab.Content.IsSaved == false)
            {
                var dgres = MessageBox.Show(String.Format(Properties.Resources.L_SaveChangesDg, tab.Content.Name, Environment.NewLine), 
                    Properties.Resources.L_SaveChangesDgTitle, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Yes);

                if (dgres == MessageBoxResult.Cancel)
                {
                    cancelled = true;
                    return;
                }
                else if (dgres == MessageBoxResult.Yes)
                {
                    bool ok = tab.Save();
                    if (!ok)
                        return;
                }
            }
            RemoveTabPage(tab);
        }
        public ETab CurrentTab
        {
            get
            {
                var select = EditorTabs.SelectedIndex;
                if (select == -1)
                    return null;
                return EditorMgr.Tabs[select];
            }
            set { value.Focus(); }
        }
        public bool CloseAllTabs()
        {
            while (EditorMgr.Tabs.Count > 0)
            {
                if (!CloseTab(EditorMgr.Tabs[0]))
                    return false;
            }
            SetStatus(Properties.Resources.L_TabsClosed);
            return true;
        }

        public void ExecuteIDECommand(eIDECommand cmd, string cmdArgs = null)
        {
            // Handling a command to do something
            switch (cmd)
            {
                case eIDECommand.OpenEmptyTab:
                    this.Activate();
                    var tab = ETab.CreateNew(EditorMgr, cmdArgs == "guiWorkbench" ? SupportedFileTypes.IvmpGuiWindow : null);
                    EditorMgr.Tabs.Add(tab);
                    OnTabOpened(tab);
                    break;

                case eIDECommand.OpenFileAuto:
                    this.Activate();
                    try
                    {
                        var openFile = new FileInfo(cmdArgs.Replace("\"", string.Empty));
                        var doFileOpen = true;

                        if (!openFile.Exists)
                            doFileOpen = false;
                        else
                            // Check if we already have file with same file name opened
                            foreach (var etab in EditorMgr.Tabs)
                                if (etab.IsDocument && etab.Document.File.FullName == openFile.FullName)
                                {
                                    etab.Focus();
                                    doFileOpen = false;
                                }

                        if (doFileOpen)
                        {
                            var etab = ETab.OpenFile(EditorMgr, openFile);
                            EditorMgr.Tabs.Add(etab);
                            OnTabOpened(etab);

                            if (EditorMgr.Tabs.Count == 2 && EditorMgr.Tabs[0].IsDocument &&
                                EditorMgr.Tabs[0].Document.IsDesiredNew && EditorMgr.Tabs[0].Document.IsSaved)
                                CloseTab(EditorMgr.Tabs[0]);
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, string.Format(Properties.Resources.L_ErrorFileOpen, ex.GetType().Name));
                        return;
                    }
                    break;

                default:
                    throw new ArgumentException("Unknown command (ExecuteIDECommand): " + cmd);
            }
        }
        public new bool Activate()
        {
            // Ensure window is not minimized:
            if(WindowState == WindowState.Minimized)
                WindowState = WindowState.Normal;

            return base.Activate();
        }
        public void SetStatus(string status)
        {
            textStatus.Text = status;

            // Force status string to refresh
            // FIXME: Temporary, until some operations is not threaded!
            Action dummy = delegate() { };
            this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, dummy);
        }
        public void UpdateTitle()
        {
            var tab = CurrentTab;
            this.Title = String.Format("{0}IV:MP GUI Editor v{1}", tab != null && tab.IsDocument ? tab.Document.File.Name + " - " : null, Common.AppVersionString);
        }
        public void UpdateFavoriteEncodings(Encoding[] encodings = null)
        {
            if (encodings == null)
                encodings = Settings.FavoriteEncodings;

            menuChangeEncodings.Items.Clear();

            if (encodings.Length > 0)
                foreach (var encd in encodings)
                {
                    var mi = new MenuItem();
                    mi.Header = string.Format("{0} - cp{1}", encd.EncodingName, encd.CodePage);
                    mi.Click += Menu_ToolsChangeEncoding;
                    menuChangeEncodings.Items.Add(mi);
                }
            else
            {
                // If no favorite encodings set, add 'Settings' link
                var mi = new MenuItem();
                mi.Header = string.Format(Properties.Resources.L_Settings + "...");
                mi.Click += Menu_HelpSettings;
                menuChangeEncodings.Items.Add(mi);
            }

            menuChangeEncodings.Items.Add(new Separator());
            var deft = new MenuItem();
            deft.Header = "Unicode (UTF-8) - cp65001";
            deft.Click += Menu_ToolsChangeEncoding;
            menuChangeEncodings.Items.Add(deft);
        }
        public void UpdateRecentFiles(List<string> recentFiles = null)
        {
            if (recentFiles == null)
                recentFiles = Settings.RecentFiles;

            menuRecentFiles.Items.Clear();

            if (recentFiles != null)
                for (int i = 0; i < recentFiles.Count; i++)
                {
                    var mi = new MenuItem();
                    mi.Header = recentFiles[i].Replace("_", "__");
                    mi.Click += Menu_FileOpenRecent;
                    menuRecentFiles.Items.Add(mi);
                }
            else
            {
                var mi = new MenuItem() { Header = Properties.Resources.L_HistoryEmpty, IsEnabled = false };
                menuRecentFiles.Items.Add(mi);
            }
        }
        public void RemoveTabPage(ETab tab)
        {
            if (tab.IsDocument)
            {
                (EditorMgr.FindReplaceMgr.Editors as List<FindReplace.IEditor>).Remove(tab.Document.FindReplaceAdapter);
                SetStatus(string.Format(Properties.Resources.L_TabClosed, tab.Document.File.Name));
            }

            EditorTabs.Items.Remove(tab.TabPage);
            EditorMgr.Tabs.Remove(tab);
        }

        // Events:
        private void OnTabOpened(ETab tab)
        {
            // Add document's tab to our TabControl
            EditorTabs.Items.Add(tab.TabPage);

            if (tab.IsDocument)
            {
                // Init find & replace for this document:
                var findReplaceMgr = EditorMgr.FindReplaceMgr;
                (findReplaceMgr.Editors as List<FindReplace.IEditor>).Add(tab.Document.FindReplaceAdapter);
                findReplaceMgr.CurrentEditor = tab.Document.FindReplaceAdapter;

                if (tab.Document.IsDesiredNew == false)
                {
                    // Save this file to recent files submenu and update it:
                    UpdateRecentFiles(  Settings.AddRecentFilesTop(tab.Document.File.FullName)    );
                }

                SetStatus(string.Format(Properties.Resources.L_OpenPost, tab.Document.File.Name, tab.Document.TextEncoding.EncodingName));
            }

            tab.SetUpEvents();
            tab.Focus();
        }
        private void wndCodeEditor_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!EditorMgr.CheckExitAllowed())
                e.Cancel = true;
            else if (!CloseAllTabs())
                e.Cancel = true;
        }
        private void Window_Closed(object sender, EventArgs e)
        {
            if (Settings.RememberWindowState)
            {
                // Save desired window size to config file:
                Settings.MainWindowSize = this.RenderSize;
                Settings.MainWindowMaximized = (this.WindowState == System.Windows.WindowState.Maximized);
                Settings.MainWindowPosition = new Point(this.Left, this.Top);
            }
        }
        private void EditorTabs_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Selected tab/document has changed:
            var tab = CurrentTab;
            if (tab != null)
            {
                if(tab.IsDocument)
                    EditorMgr.FindReplaceMgr.CurrentEditor = tab.Document.FindReplaceAdapter;
            }
            UpdateTitle();
        }
        private void MenuTabPage_Opened(object sender, RoutedEventArgs e)
        {
            // TODO: Check if it is a document, and if it is not, disable some menu items?
        }
        private void wndCodeEditor_WindowStateChanged(object sender, EventArgs e)
        {
            if (this.WindowState == System.Windows.WindowState.Minimized && EditorMgr.ColorPickerVisible)
                EditorMgr.ColorPicker.Hide();
        }

        // Main menu:
        #region File menu
        private void Menu_NewFile(object sender, RoutedEventArgs e)
        {
            ExecuteIDECommand(eIDECommand.OpenEmptyTab);
        }
        private void Menu_NewGuiWorkbench(object sender, RoutedEventArgs e)
        {
            ExecuteIDECommand(eIDECommand.OpenEmptyTab, "guiWorkbench");
        }
        private void Menu_FileOpen(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.OpenFileDialog dg = new System.Windows.Forms.OpenFileDialog();
            dg.Filter = SupportedFileTypes.GetFilterList(true, true, true);
            dg.FilterIndex = SupportedFileTypes.List.Length + 1; // "Any of supported file types"
            dg.AddExtension = dg.Multiselect = false;
            dg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            dg.Title = Properties.Resources.L_Open;
            if (dg.ShowDialog() == System.Windows.Forms.DialogResult.Cancel)
                return;

            ExecuteIDECommand(eIDECommand.OpenFileAuto, dg.FileName);
        }
        private void Menu_FileCloseAll(object sender, RoutedEventArgs e) { CloseAllTabs(); }
        private void Menu_FileClose(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null)
                return;

            bool cancelled = false;
            CloseTab(tab, out cancelled);
        }
        private void Menu_FileSaveAs(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null)
                return;

            if (!tab.Document.ShowSaveAsDialog())
                return;
            tab.Save();
            UpdateTitle();
        }
        private void Menu_FileSave(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null)
                return;

            tab.Save();
        }
        private void Menu_FileSaveAll(object sender, RoutedEventArgs e)
        {
            foreach (var tab in EditorMgr.Tabs)
                tab.Save();
        }
        private void Menu_FileExit(object sender, RoutedEventArgs e) { this.Close(); }
        private void Menu_FileCopyPath(object sender, ExecutedRoutedEventArgs e)
        {
            var tab = CurrentTab;
            Clipboard.SetText(tab.Document.File.FullName);// fixme: for gui workbench crash
        }
        private void Menu_FileCopyDirectoryPath(object sender, ExecutedRoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null)
                return;

            if (tab.IsDocument)
            {
                Clipboard.SetText(tab.Document.File.DirectoryName);
            }
        }
        private void Menu_FileCloseOthers(object sender, ExecutedRoutedEventArgs e)
        {
            var tab = CurrentTab;
            for (var i = 0; i < EditorMgr.Tabs.Count; i++)
            {
                if (tab == EditorMgr.Tabs[i])
                    continue;

                if (CloseTab(EditorMgr.Tabs[i]))
                    i--;
            }
        }
        private void Menu_FileOpenRecent(object sender, RoutedEventArgs e)
        {
            var mi = (MenuItem) sender;
            var fileName = (string) mi.Header;
            ExecuteIDECommand(eIDECommand.OpenFileAuto, fileName.Replace("__", "_"));
        }
        #endregion
        #region TOOLS menu
        private void Menu_ToolsCompile(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null)
                return;

            // Any compiler for this file type?
            if (tab.IsDocument)
            {
                if (tab.Document.Type.CompilerType != null)
                {
                    if (tab.Document.IsDesiredNew && !tab.Document.Save())
                        return;// save cancelled

                    // Show compiler output window and run compiler
                    var cOut = EditorMgr.CompilerOutputWindow;
                    cOut.Show();
                    cOut.StartCompilation(tab.Document);
                }
                else
                    MessageBox.Show(Properties.Resources.L_CompileNA, tab.Document.Type.Name + " - IV:MP GUI Editor " + Common.AppVersionString,
                        MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }
        private void Menu_ToolsSearchReplace(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null || !tab.IsDocument)
                return;

            // Show our search&replace manager window
            var findReplaceMgr = EditorMgr.FindReplaceMgr;
            findReplaceMgr.CurrentEditor = tab.Document.FindReplaceAdapter;
            findReplaceMgr.ShowAsFind();
        }
        private void Menu_ToolsChangeEncoding(object sender, RoutedEventArgs e)
        {
            // FIXME
            var tab = CurrentTab;
            if (tab == null || !tab.IsDocument)
                return;

            // Change (convert?) encoding of current document:
            var toEncoding_name = (sender as MenuItem).Header.ToString();
            var i = toEncoding_name.LastIndexOf("cp") + 2;
            var toEncoding_cp = ushort.Parse(toEncoding_name.Substring(i, toEncoding_name.Length - i));
            var toEncoding = Encoding.GetEncoding(toEncoding_cp);
            if (toEncoding == tab.Document.TextEncoding)
                SetStatus(string.Format(Properties.Resources.L_EncodingAlready, tab.Document.File.Name, toEncoding.EncodingName));
            else
            {
                // Convert text:
                SetStatus(Properties.Resources.L_PleaseWait);
                var txtBytes = tab.Document.TextEncoding.GetBytes(tab.Document.TextBox.Text);
                var convertedBytes = Encoding.Convert(tab.Document.TextEncoding, toEncoding, txtBytes);
                tab.Document.Text = toEncoding.GetString(convertedBytes);

                tab.Document.TextEncoding = toEncoding;
                tab.Document.TextBox.Encoding = toEncoding;
                tab.Document.IsSaved = false;
                SetStatus(string.Format(Properties.Resources.L_EncodingChanged, tab.Document.File.Name, toEncoding.EncodingName));
            }
        }
        private void Menu_ToolsColorPicker(object sender, RoutedEventArgs e)
        {
            var cpr = EditorMgr.ColorPicker;
            if (cpr.Visible)
                cpr.Focus();
            else
                cpr.Visible = true;
        }
        private void Menu_ToolsShowCodeCompletion(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null || !tab.IsDocument)
                return;

            var doc = tab.Document;
            var codeCompletion = doc.Type.CodeCompletion;
            if (codeCompletion == null)
            {
                SetStatus(Properties.Resources.L_NoFunctionsList);
                return;
            }

            var wnd = codeCompletion.CreateWindow(tab.Document.TextBox.TextArea);
            wnd.Show();
            wnd.Width *= 1.3d;//fixme
        }
        private void Menu_ToolsCopy(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null || !tab.IsDocument)
                return;

            tab.Document.TextBox.Copy();
        }
        private void Menu_ToolsCut(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null || !tab.IsDocument)
                return;

            tab.Document.TextBox.Cut();
        }
        private void Menu_ToolsPaste(object sender, RoutedEventArgs e)
        {
            var tab = CurrentTab;
            if (tab == null || !tab.IsDocument)
                return;

            tab.Document.TextBox.Paste();
        }
        #endregion
        #region Resources & wiki
        private void MenuResources_IvmpWiki(object sender, RoutedEventArgs e)
        {
            Common.OpenWebUrl(Properties.Resources.L_LinkIvMpWiki);
        }
        private void MenuResources_SampWiki(object sender, RoutedEventArgs e)
        {
            Common.OpenWebUrl(Properties.Resources.L_LinkSaMpWiki);
        }
        private void MenuResources_MtaSaWiki(object sender, RoutedEventArgs e)
        {
            Common.OpenWebUrl(Properties.Resources.L_LinkMtaSaWiki);
        }
        #endregion
        #region Help menu
        private void Menu_HelpForum(object sender, RoutedEventArgs e)
        {
            Common.OpenWebUrl("http://forum.iv-multiplayer.com/index.php/topic,4680.0.html");
        }
        private void Menu_HelpCheckForUpdates(object sender, RoutedEventArgs e)
        {
            try
            {
                SetStatus(Properties.Resources.L_PleaseWait);
                var vInf = WebUpdates.GrabUpdatesInfo();

                if (vInf.IsUpdateAvailable)
                {
                    // Confirm update:
                    var dgResult = MessageBox.Show(string.Format(Properties.Resources.L_UpdatesFound,
                        Environment.NewLine, Common.AppVersion, vInf.LatestVersion),
                        "IV:MP GUI Editor - Version", MessageBoxButton.OKCancel, MessageBoxImage.Information);
                    if (dgResult != MessageBoxResult.OK)
                        return;

                    // Close all tabs:
                    if (CloseAllTabs() == false)
                        return;

                    // Update & exit app
                    Hide();
                    WebUpdates.DoUpdate(vInf);
                    App.Current.Shutdown();
                }
                else
                    MessageBox.Show(string.Format(Properties.Resources.L_NoUpdatesAvailable,
                        Environment.NewLine), "IV:MP GUI Editor v" + Common.AppVersionString, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format(Properties.Resources.L_ErrorU, ex.Message));
            }
        }
        private void Menu_HelpAbout(object sender, RoutedEventArgs e)
        {
            // fixme
        }
        private void Menu_HelpSettings(object sender, RoutedEventArgs e)
        {
            (new wndOptions()).ShowDialog();
        }
        #endregion
    }

    public static class IDECommands
    {
        // Defining all IDE/menu commands:
        public static RoutedCommand FileNew = new RoutedCommand();
        public static RoutedCommand FileOpen = new RoutedCommand();
        public static RoutedCommand FileClose = new RoutedCommand();
        public static RoutedCommand FileCloseAll = new RoutedCommand();
        public static RoutedCommand FileSave = new RoutedCommand();
        public static RoutedCommand FileSaveAll = new RoutedCommand();
        public static RoutedCommand FileCopyPath = new RoutedCommand();
        public static RoutedCommand FileCopyDirectoryPath = new RoutedCommand();
        public static RoutedCommand FileCloseOthers = new RoutedCommand();
        public static RoutedCommand ToolsCompile = new RoutedCommand();
        public static RoutedCommand ToolsSearchReplace = new RoutedCommand();
        public static RoutedCommand ToolsColorPicker = new RoutedCommand();
        public static RoutedCommand ToolsShowCodeCompletion = new RoutedCommand();
        public static RoutedCommand HelpAbout = new RoutedCommand();
        public static RoutedCommand HelpCheckForUpdates = new RoutedCommand();
    }
}
